﻿2026-08-08T19:20:00.3093374Z ##[group]Run set -euo pipefail
2026-08-08T19:20:00.3093672Z [36;1mset -euo pipefail[0m
2026-08-08T19:20:00.3093931Z [36;1mmkdir -p /tmp/financemanager-phase3-backend/runtime[0m
2026-08-08T19:20:00.3094329Z [36;1mpython -m pytest tests -q | tee /tmp/financemanager-phase3-backend/pytest.log[0m
2026-08-08T19:20:00.3094874Z [36;1mpassed_count=$(sed -nE 's/.*(^|[^0-9])([0-9]+) passed.*/\2/p' /tmp/financemanager-phase3-backend/pytest.log | tail -n 1)[0m
2026-08-08T19:20:00.3095295Z [36;1mtest -n "${passed_count}"[0m
2026-08-08T19:20:00.3096049Z [36;1mtest "${passed_count}" -ge 667[0m
2026-08-08T19:20:00.3136368Z shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
2026-08-08T19:20:00.3136653Z env:
2026-08-08T19:20:00.3136820Z   PYTHONPATH: src
2026-08-08T19:20:00.3137010Z   JARVIS_FINANCE_ENV: test
2026-08-08T19:20:00.3137299Z   JARVIS_FINANCE_DB_PATH: /tmp/financemanager-phase3-backend/finance.sqlite3
2026-08-08T19:20:00.3137817Z   JARVIS_FINANCE_RUNTIME_DIR: /tmp/financemanager-phase3-backend/runtime
2026-08-08T19:20:00.3138251Z   JARVIS_FINANCE_WRITE_MODE: disabled
2026-08-08T19:20:00.3138520Z   pythonLocation: /opt/hostedtoolcache/Python/3.11.15/x64
2026-08-08T19:20:00.3138848Z   PKG_CONFIG_PATH: /opt/hostedtoolcache/Python/3.11.15/x64/lib/pkgconfig
2026-08-08T19:20:00.3139177Z   Python_ROOT_DIR: /opt/hostedtoolcache/Python/3.11.15/x64
2026-08-08T19:20:00.3139477Z   Python2_ROOT_DIR: /opt/hostedtoolcache/Python/3.11.15/x64
2026-08-08T19:20:00.3139779Z   Python3_ROOT_DIR: /opt/hostedtoolcache/Python/3.11.15/x64
2026-08-08T19:20:00.3140087Z   LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.11.15/x64/lib
2026-08-08T19:20:00.3140339Z ##[endgroup]
2026-08-08T19:20:15.7225971Z ........................................................................ [  7%]
2026-08-08T19:20:25.7698698Z ........................................................................ [ 14%]
2026-08-08T19:20:35.5819170Z ........................................................................ [ 21%]
2026-08-08T19:20:47.7936784Z ........................................................................ [ 28%]
2026-08-08T19:20:58.1066869Z ........................................................................ [ 35%]
2026-08-08T19:21:06.6843217Z ........................................................................ [ 43%]
2026-08-08T19:21:15.2322580Z ........................................................................ [ 50%]
2026-08-08T19:21:30.5036341Z ........................................................................ [ 57%]
2026-08-08T19:21:41.1991854Z ........................................................................ [ 64%]
2026-08-08T19:22:01.7938087Z ........................................................................ [ 71%]
2026-08-08T19:22:24.3609854Z ........................................................................ [ 78%]
2026-08-08T19:22:36.0479210Z ........................................................................ [ 86%]
2026-08-08T19:22:52.8206856Z ..FF.................................................................... [ 93%]
2026-08-08T19:23:04.1556359Z ....................................................................     [100%]
2026-08-08T19:23:04.1557032Z =================================== FAILURES ===================================
2026-08-08T19:23:04.1557949Z _ test_confirm_is_fingerprint_bound_idempotent_audited_and_does_not_activate_rule _
2026-08-08T19:23:04.1558678Z 
2026-08-08T19:23:04.1559208Z     def test_confirm_is_fingerprint_bound_idempotent_audited_and_does_not_activate_rule():
2026-08-08T19:23:04.1559930Z         conn = database()
2026-08-08T19:23:04.1560414Z         add_bank_row(conn, "bank-1", "2026-01-30", "600")
2026-08-08T19:23:04.1560937Z         result = preview(conn)
2026-08-08T19:23:04.1561382Z         request = confirmation_request(result)
2026-08-08T19:23:04.1561824Z     
2026-08-08T19:23:04.1562258Z         confirmed = confirm_truewealth_bank_payments(conn, request)
2026-08-08T19:23:04.1562887Z         repeated = confirm_truewealth_bank_payments(conn, request)
2026-08-08T19:23:04.1563877Z         altered_replay = {**request, "recipient_name": "Different Recipient"}
2026-08-08T19:23:04.1564423Z     
2026-08-08T19:23:04.1564804Z         assert confirmed["written_cashflows"] == 1
2026-08-08T19:23:04.1565306Z         assert confirmed["rule_activated"] is False
2026-08-08T19:23:04.1566004Z         assert repeated["idempotent"] is True
2026-08-08T19:23:04.1566642Z         with pytest.raises(ValueError, match="already used with a different payload"):
2026-08-08T19:23:04.1567561Z             confirm_truewealth_bank_payments(conn, altered_replay)
2026-08-08T19:23:04.1568207Z         TrueWealthBankPaymentConfirmResponse.model_validate(confirmed)
2026-08-08T19:23:04.1568906Z         TrueWealthBankPaymentConfirmResponse.model_validate(repeated)
2026-08-08T19:23:04.1569838Z         assert conn.execute("SELECT COUNT(*) FROM transactions WHERE source_type='truewealth_raiffeisen_cashflow_v1'").fetchone()[0] == 1
2026-08-08T19:23:04.1570910Z         assert conn.execute("SELECT COUNT(*) FROM audit_log WHERE action='recipient_rule_activated'").fetchone()[0] == 0
2026-08-08T19:23:04.1571840Z         assert conn.execute("SELECT COUNT(*) FROM performance_cashflow_coverage").fetchone()[0] == 0
2026-08-08T19:23:04.1572500Z         audit_payload = conn.execute(
2026-08-08T19:23:04.1573126Z             "SELECT new_values_json FROM audit_log WHERE action='recipient_rule_and_history_confirmed'"
2026-08-08T19:23:04.1573783Z         ).fetchone()[0]
2026-08-08T19:23:04.1574198Z         assert "Dauerauftrag" not in audit_payload
2026-08-08T19:23:04.1574714Z         assert ATTESTATION not in audit_payload
2026-08-08T19:23:04.1575208Z         assert '"description"' not in audit_payload
2026-08-08T19:23:04.1576234Z         assert conn.execute("SELECT transaction_type FROM budget_transactions WHERE budget_transaction_id='bank-1'").fetchone()[0] == "transfer"
2026-08-08T19:23:04.1577473Z         activity = load_scope_activities(conn, account_ids=["tw-total"], to_date="2026-07-31", data_cutoff="2026-08-04T00:00:00+00:00")
2026-08-08T19:23:04.1578396Z >       assert [(row.kind, str(row.net)) for row in activity] == [("external_deposit", "600")]
2026-08-08T19:23:04.1579073Z E       AssertionError: assert [] == [('external_deposit', '600')]
2026-08-08T19:23:04.1579573Z E         
2026-08-08T19:23:04.1580003Z E         Right contains one more item: ('external_deposit', '600')
2026-08-08T19:23:04.1580508Z E         
2026-08-08T19:23:04.1580843Z E         Full diff:
2026-08-08T19:23:04.1581193Z E         + []
2026-08-08T19:23:04.1581550Z E         - [
2026-08-08T19:23:04.1581896Z E         -     (
2026-08-08T19:23:04.1582263Z E         -         'external_deposit',
2026-08-08T19:23:04.1582695Z E         -         '600',
2026-08-08T19:23:04.1583076Z E         -     ),
2026-08-08T19:23:04.1583419Z E         - ]
2026-08-08T19:23:04.1583675Z 
2026-08-08T19:23:04.1584041Z tests/unit/test_sprint20d_truewealth_productization.py:477: AssertionError
2026-08-08T19:23:04.1584865Z _ test_confirmed_payment_enters_canonical_ttwror_and_xirr_only_after_separate_coverage _
2026-08-08T19:23:04.1585409Z 
2026-08-08T19:23:04.1586216Z     def test_confirmed_payment_enters_canonical_ttwror_and_xirr_only_after_separate_coverage():
2026-08-08T19:23:04.1586870Z         conn = database()
2026-08-08T19:23:04.1587289Z         add_bank_row(conn, "bank-1", "2026-01-30", "600")
2026-08-08T19:23:04.1587774Z         bank_preview = preview(conn)
2026-08-08T19:23:04.1588346Z         confirm_truewealth_bank_payments(conn, confirmation_request(bank_preview))
2026-08-08T19:23:04.1588961Z         for snapshot_id, day, value in (
2026-08-08T19:23:04.1589404Z             ("open", "2026-01-01", "1000"),
2026-08-08T19:23:04.1589840Z             ("flow", "2026-01-30", "1600"),
2026-08-08T19:23:04.1590274Z             ("close", "2026-07-31", "1800"),
2026-08-08T19:23:04.1590698Z         ):
2026-08-08T19:23:04.1591045Z             conn.execute(
2026-08-08T19:23:04.1591953Z                 """INSERT INTO account_value_snapshots(snapshot_id,account_id,valuation_date,total_value_chf,currency,source_type,quality_status,created_at,source_reference,is_active)
2026-08-08T19:23:04.1593285Z                    VALUES(?,?,?,?,'CHF','truewealth_official_import','ok',?,?,1)""",
2026-08-08T19:23:04.1593900Z                 (snapshot_id, "tw-total", day, value, NOW, snapshot_id),
2026-08-08T19:23:04.1594404Z             )
2026-08-08T19:23:04.1594770Z         refreshed_bank_preview = preview(conn)
2026-08-08T19:23:04.1595339Z         activation_preview = build_truewealth_activation_performance_preview(
2026-08-08T19:23:04.1596299Z             conn,
2026-08-08T19:23:04.1596688Z             bank_preview=refreshed_bank_preview,
2026-08-08T19:23:04.1597288Z             period_from="2026-01-01",
2026-08-08T19:23:04.1597763Z             period_to="2026-07-31",
2026-08-08T19:23:04.1598155Z         )
2026-08-08T19:23:04.1598574Z         assert refreshed_bank_preview["counts"]["existing"] == 1
2026-08-08T19:23:04.1599176Z         assert refreshed_bank_preview["cashflows_to_write"] == []
2026-08-08T19:23:04.1599838Z         assert Decimal(activation_preview["deposit_total_chf"]) == Decimal(600)
2026-08-08T19:23:04.1600456Z         assert len(activation_preview["deposits"]) == 1
2026-08-08T19:23:04.1600935Z         set_performance_cashflow_coverage(
2026-08-08T19:23:04.1601437Z             conn,
2026-08-08T19:23:04.1601799Z             account_id="tw-total",
2026-08-08T19:23:04.1602230Z             coverage_from="2026-01-01",
2026-08-08T19:23:04.1602656Z             coverage_to="2026-07-31",
2026-08-08T19:23:04.1603092Z             status="complete",
2026-08-08T19:23:04.1603510Z             source="explicit_test_attestation",
2026-08-08T19:23:04.1604111Z             note="All external cashflows independently attested for this test period.",
2026-08-08T19:23:04.1604722Z             recorded_at=NOW,
2026-08-08T19:23:04.1605102Z         )
2026-08-08T19:23:04.1605444Z         conn.commit()
2026-08-08T19:23:04.1605943Z     
2026-08-08T19:23:04.1606315Z         performance = build_portfolio_performance(
2026-08-08T19:23:04.1606773Z             conn,
2026-08-08T19:23:04.1607126Z             from_date="2026-01-01",
2026-08-08T19:23:04.1607537Z             to_date="2026-07-31",
2026-08-08T19:23:04.1607955Z             account_id="tw-total",
2026-08-08T19:23:04.1608389Z             data_cutoff="2026-08-04T00:00:00+00:00",
2026-08-08T19:23:04.1608820Z         )
2026-08-08T19:23:04.1609144Z     
2026-08-08T19:23:04.1609540Z         summary = cast(dict[str, Any], performance["summary"])
2026-08-08T19:23:04.1610132Z         quality = cast(dict[str, dict[str, Any]], performance["quality"])
2026-08-08T19:23:04.1610788Z >       assert Decimal(str(summary["net_external_cashflows"])) == Decimal(600)
2026-08-08T19:23:04.1611413Z E       AssertionError: assert Decimal('0') == Decimal('600')
2026-08-08T19:23:04.1611918Z E        +  where Decimal('0') = Decimal('0')
2026-08-08T19:23:04.1612366Z E        +    where '0' = str('0')
2026-08-08T19:23:04.1612798Z E        +  and   Decimal('600') = Decimal(600)
2026-08-08T19:23:04.1613152Z 
2026-08-08T19:23:04.1613521Z tests/unit/test_sprint20d_truewealth_productization.py:539: AssertionError
2026-08-08T19:23:04.1614166Z =============================== warnings summary ===============================
2026-08-08T19:23:04.1614901Z ../../../../../opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/fastapi/testclient.py:1
2026-08-08T19:23:04.1616356Z   /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
2026-08-08T19:23:04.1617602Z     from starlette.testclient import TestClient as TestClient  # noqa
2026-08-08T19:23:04.1618049Z 
2026-08-08T19:23:04.1618424Z -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
2026-08-08T19:23:04.1619074Z =========================== short test summary info ============================
2026-08-08T19:23:04.1620404Z FAILED tests/unit/test_sprint20d_truewealth_productization.py::test_confirm_is_fingerprint_bound_idempotent_audited_and_does_not_activate_rule - AssertionError: assert [] == [('external_deposit', '600')]
2026-08-08T19:23:04.1621693Z   
2026-08-08T19:23:04.1622108Z   Right contains one more item: ('external_deposit', '600')
2026-08-08T19:23:04.1622590Z   
2026-08-08T19:23:04.1622917Z   Full diff:
2026-08-08T19:23:04.1623277Z   + []
2026-08-08T19:23:04.1623596Z   - [
2026-08-08T19:23:04.1623921Z   -     (
2026-08-08T19:23:04.1624264Z   -         'external_deposit',
2026-08-08T19:23:04.1624822Z   -         '600',
2026-08-08T19:23:04.1625161Z   -     ),
2026-08-08T19:23:04.1625485Z   - ]
2026-08-08T19:23:04.1626612Z FAILED tests/unit/test_sprint20d_truewealth_productization.py::test_confirmed_payment_enters_canonical_ttwror_and_xirr_only_after_separate_coverage - AssertionError: assert Decimal('0') == Decimal('600')
2026-08-08T19:23:04.1627698Z  +  where Decimal('0') = Decimal('0')
2026-08-08T19:23:04.1628128Z  +    where '0' = str('0')
2026-08-08T19:23:04.1628524Z  +  and   Decimal('600') = Decimal(600)
2026-08-08T19:23:04.1628998Z 2 failed, 1002 passed, 1 warning in 181.93s (0:03:01)
2026-08-08T19:23:04.6296814Z ##[error]Process completed with exit code 1.
